///////////////////////////////////////////////////////////////////////////////
//
// Licensed under the MIT License
//
// Copyright (c) 2010 Chris Colinsky
// All Rights Reserved
//
// version	0.5
// http://code.google.com/p/sugarcookie/
// http://code.google.com/p/sugarcookie/wiki/License
//
////////////////////////////////////////////////////////////////////////////////

package sugarcookie.collections {
	import flash.events.Event;
	import flash.events.EventDispatcher;
	
	import mx.collections.CursorBookmark;
	import mx.collections.ICollectionView;
	import mx.collections.IViewCursor;
	import sugarcookie.error.ApplicationError;
	import sugarcookie.events.GenericEvent;

	/**
	 * Uses Flex IViewCursor to provide functionality for associative array based EnhancedArrayCollection.
	 */
	public class EnhancedCursor extends EventDispatcher implements IViewCursor {
		public static var AFTER_LAST:String = "afterLast";
		public static var BEFORE_FIRST:String = "beforeFirst";
		public static var CURSOR_UPDATE:String = "cursorUpdate";
		
		private var _collection:EnhancedArrayCollection;
		private var _afterLast:Boolean = false;
		private var _beforeFirst:Boolean = false;
		private var _marker:Object;
		
		public function EnhancedCursor(ac:EnhancedArrayCollection,m:Object=null) {
			_collection = ac;
			
			if (!_collection.isAssociative() && m==null) _marker = 0;
			else _marker = m;
		}
		
		public function get beforeFirst():Boolean {
			return _beforeFirst;
		}
		
		public function seek(bookmark:CursorBookmark, offset:int=0.0, prefetch:int=0.0):void {
			//not supported at this time :(
		}
		
		public function remove():Object {
			var val:Object = current;
			
			if (_marker==null) {
				throw new ApplicationError("Marker has not been set");
				return null;
			} else if (isMarkerString()) {
				return _collection.removeItemAtKey(String(_marker));
			} else {
				return _collection.removeItemAt(int(_marker));
			}
		}
		
		public function findAny(values:Object):Boolean {
			//not supported at this time :(
			return false;
		}
		
		public function get current():Object {
			if (_marker==null) return null;
			else return _collection.source[_marker];
		}
		
		public function get bookmark():CursorBookmark {
			//not supported at this time :(
			return null;
		}
		
		public function get view():ICollectionView {
			return _collection;
		}
		
		public function moveNext():Boolean {
			if (_collection.isAssociative()) {
				throw new ApplicationError("ArrayCollection source is asociative, unable to traverse collection.");
				return false;
			}
			
			var index:Number = int(_marker);
			index++;
			_beforeFirst = false;
			if (index==_collection.length) _afterLast = true;
			update(index);
			
			return !_afterLast;
		}
		
		public function get afterLast():Boolean {
			return _afterLast;
		}
		
		public function findLast(values:Object):Boolean {
			//not supported at this time :(
			return false;
		}
		
		public function insert(item:Object):void {
			if (_collection.isAssociative()) {
				throw new ApplicationError("ArrayCollection source is asociative, unable to insert.");
				return;
			}
			
			var insertIndex:int = int(_marker)-1;
			_collection.addItemAt(item,insertIndex);
		}
		
		public function movePrevious():Boolean {
			if (_collection.isAssociative()) throw new ApplicationError("ArrayCollection source is asociative, unable to traverse collection.");
			
			var index:Number = int(_marker);
			index--;
			_afterLast = false;
			if (index<0) _beforeFirst = true;
			update(index);
			
			return !_beforeFirst;
		}
		
		public function findFirst(values:Object):Boolean {
			//not supported at this time :(
			return false;
		}
		
		private function update(m:Object):void {
			if (_afterLast || _beforeFirst) {
				_marker = null;
				if (_afterLast) dispatchEvent(new GenericEvent(AFTER_LAST,false,false,null,true));
				else if (_beforeFirst) dispatchEvent(new GenericEvent(BEFORE_FIRST,false,false,null,true));
			} else {
				_marker = m;
				dispatchEvent(new GenericEvent(CURSOR_UPDATE,false,false,null,true));
			}
		}
		
		private function isMarkerString():Boolean {
			return isNaN(parseInt(String(_marker)));
		}
	}
}